// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2023, Huaihan He <hehuaihan@huawei.com>
 */

#include "qcom_subsys_notify.h"

/**
 * rproc_get_by_name() - acquire rproc instance by rproc name
 * @name: name of the rproc device
 *
 * Returns the rproc instance, or NULL if not found.
 */
struct rproc *rproc_get_by_name(const char *name)
{
	struct mutex *rproc_list_mutex = get_rproc_list_mutex();
	struct list_head *rproc_list = get_rproc_list();
	struct rproc *rproc = NULL, *r;

	mutex_lock(rproc_list_mutex);
	list_for_each_entry(r, rproc_list, node) {
		if (!strcmp(r->name, name)) {
			rproc = r;
			break;
		}
	}
	mutex_unlock(rproc_list_mutex);

	return rproc;
}
EXPORT_SYMBOL(rproc_get_by_name);

/**
 * rproc_register_subsys_notifier() - register for subsys start, stop events
 * @name: name of the rproc device
 * @nb - notifier bloc
 * @atomic-nb - atomic notifier block
 * Returns 0 on success else error
 */
int rproc_register_subsys_notifier(const char *name, struct notifier_block *nb,
		struct notifier_block *atomic_nb)
{
	struct rproc *rproc = rproc_get_by_name(name);
	int ret = 0;

	if (!rproc)
		return -ENODEV;

	if (atomic_nb)
		ret = atomic_notifier_chain_register(&rproc->atomic_nlist,
								atomic_nb);
	if (!ret)
		ret = blocking_notifier_chain_register(&rproc->nlist, nb);

	return ret;
}
EXPORT_SYMBOL(rproc_register_subsys_notifier);
/**
 * rproc_unregister_subsys_notifier() - register for subsys start, stop events
 * @name: name of the rproc device
 * @nb - notifier bloc
 * @atomic-nb - atomic notifier block
 * Returns 0 on success else error
 */
int rproc_unregister_subsys_notifier(const char *name, struct notifier_block *nb,
		struct notifier_block *atomic_nb)
{
	struct rproc *rproc = rproc_get_by_name(name);
	int ret = 0;

	if (!rproc)
		return -ENODEV;

	if (atomic_nb)
		ret = atomic_notifier_chain_unregister(&rproc->atomic_nlist,
								atomic_nb);
	if (!ret)
		ret = blocking_notifier_chain_unregister(&rproc->nlist, nb);

	return ret;
}
EXPORT_SYMBOL(rproc_unregister_subsys_notifier);

/**
 * rproc_subsys_notify() - notify sub system event
 * @rproc rproc instance
 * @event, event to be notified
 * @atomic to be notified as task or atomic context
 */
void rproc_subsys_notify(struct rproc *rproc, int event, bool atomic)
{
	struct platform_device *pdev;

	pdev = of_find_device_by_node(rproc->dev.parent->of_node);
	if (!atomic && (event == SUBSYS_AFTER_POWERUP))
		msleep(100);

	if (atomic)
		atomic_notifier_call_chain(&rproc->atomic_nlist, event, pdev);
	else
		blocking_notifier_call_chain(&rproc->nlist, event, pdev);
}

/**
 * rproc_notifier_head_init() - init nlist head
 * @rproc rproc instance
 */
void rproc_notifier_head_init(struct rproc *rproc)
{
	ATOMIC_INIT_NOTIFIER_HEAD(&rproc->atomic_nlist);
	BLOCKING_INIT_NOTIFIER_HEAD(&rproc->nlist);
}
